package se.thoka.twt.client.form;

import java.util.List;

import se.thoka.twt.client.event.HasRequestSubmitHandlers;
import se.thoka.twt.client.event.RequestSubmitEvent;
import se.thoka.twt.client.event.RequestSubmitHandler;
import se.thoka.twt.client.utils.CSS;
import se.thoka.twt.client.utils.HasLabel;
import se.thoka.twt.client.utils.UniqueList;
import se.thoka.twt.client.utils.validation.HasValidation;
import se.thoka.twt.client.utils.validation.HasValidationHandlers;
import se.thoka.twt.client.utils.validation.Validation;
import se.thoka.twt.client.utils.validation.ValidationEvent;
import se.thoka.twt.client.utils.validation.ValidationHandler;
import se.thoka.twt.shared.utils.HasId;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.Widget;

public abstract class TWTFormField<T extends Widget> extends Composite implements HasId, HasText, HasLabel, HasChangeHandlers, HasEnabled, HasValidation, HasValidationHandlers, HasRequestSubmitHandlers {
	
	public static final String FORM_FIELD_LABEL_STYLE = "twt-FormFieldLabel";
	
	private static int generatedIdCounter;
	private String id, label;
	private SimpleEventBus eventBus;
	private ChangeEvent changeEvent;
	
	private boolean enabled;
	private List<Validation> validations;
	private T field;
	
	public TWTFormField() {
		this(generateId());
	}
	
	public TWTFormField(String id) {
		this(id, null);
	}
	
	public TWTFormField(HasTWTFormFieldInfo info) {
		this(info.name(), info.label());
	}
	
	public TWTFormField(String id, String label) {
		if (id == null) {
			throw new NullPointerException("Identifier must not be null");
		}
		
		this.id = id;
		this.label = label;
		this.eventBus = new SimpleEventBus();
		this.enabled = true;
		this.validations = new UniqueList<Validation>();
		initWidget(field = createField());
		CSS.setStyle(this, TWTFormFieldResources.getStyle().twtFormField(), true);
	}
	
	@Override
	public HandlerRegistration addChangeHandler(ChangeHandler handler) {
		return addHandler(ChangeEvent.getType(), handler);
	}
	
	@Override
	public HandlerRegistration addRequestSubmitHandler(RequestSubmitHandler handler) {
		return addHandler(RequestSubmitEvent.getType(), handler);
	}
	
	@Override
	public void addValidation(Validation validation) {
		validations.add(validation);
	}
	
	@Override
	public HandlerRegistration addValidationHandler(ValidationHandler handler) {
		return addHandler(ValidationEvent.getType(), handler);
	}
	
	@Override
	public void fireEvent(GwtEvent<?> event) {
		eventBus.fireEvent(event);
	}
	
	@Override
	public String getId() {
		return id;
	}
	
	@Override
	public String getLabel() {
		return label;
	}
	
	@Override
	public boolean isEnabled() {
		return enabled;
	}
	
	public void markInvalid(String message) {
		setTitle(message);
		CSS.setStyle(this, TWTFormFieldResources.getStyle().twtFormFieldInvalid(), true);
	}
	
	@Override
	public boolean removeValidation(Validation validation) {
		return validations.remove(validation);
	}
	
	public void resetValidation() {
		setTitle(null);
		CSS.setStyle(this, TWTFormFieldResources.getStyle().twtFormFieldInvalid(), false);
	}
	
	@Override
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
		CSS.setStyle(this, TWTFormFieldResources.getStyle().twtFormFieldDisabled(), !enabled);
	}
	
	@Override
	public void setLabel(String label) {
		this.label = label;
	}
	
	@Override
	public boolean validate() {
		boolean valid = true;
		resetValidation();
		if (isEnabled()) {
			String value = getText();
		
			for (Validation v : validations) {
				boolean vValid = v.validate(value);
				if (valid && !vValid) {
					markInvalid(v.getDescription());
					valid = false;
				}
				
				fireEvent(new ValidationEvent(this, valid, v.getDescription()));
			}
		}
		
		return valid;
	}
	
	protected <H extends EventHandler>HandlerRegistration addHandler(Type<H> type, H handler) {
		return eventBus.addHandler(type, handler);
	}
	
	protected abstract T createField();
	
	protected void fireChangeEvent() {
		if (changeEvent == null) {
			changeEvent = new ChangeEvent() {
				@Override
				public Object getSource() {
					return TWTFormField.this;
				}
			};
		}
		
		fireEvent(changeEvent);
	}
	
	protected T getField() {
		if (field == null) {
			field = createField();
		}
		
		return field;
	}
	
	protected void requestSubmit() {
		fireEvent(new RequestSubmitEvent());
	}
	
	protected static String generateId() {
		return "twtFormField-" + generatedIdCounter++;
	}
	
	protected class ChangeHandlerProxy implements ChangeHandler {
		public ChangeHandlerProxy() {}
		@Override
		public void onChange(ChangeEvent event) {
			fireChangeEvent();
		}
	}
}
